一个 TCL-TK 表单生成器





5.00/5 (1投票)
一个 Tcl-Tk 表单生成器,可以单独使用,也可以为那些活动可能很复杂的语言提供快速的表单生成。
引言
本文介绍了一个用 Tcl - Tk 开发的表单生成器[1] (TK FormGen),用于创建和管理表单,从简单的消息框到具有文本字段、组合框、单选按钮等等的相对复杂的表单。TK FormGen 可以被 TCL 应用程序使用,或者被其他语言调用,特别是那些没有原生 GUI 的语言;这里,与其对产品进行详尽的介绍[2],不如说明它如何被其他语言使用。
使用程序
表单生成器由脚本fGen.tcl和fGenProc.tcl组成,前者包含生成表单的过程,后者包含一些过程;后者会被fGen.tcl自动包含。
表单生成器可以被调用
- 通过 Tcl 脚本
proc cBack {data} { foreach id [dict keys $data] {puts "$id\t : [dict get $data $id]"} exit } append src [file dirname [file normalize [info script ]]] "/fGen.tcl" source $src set ask "Title,Form generalities;Ground,silver;" append ask "CMB,Theme,,,|alt|clam|classic|default|vista|winnative|xpnative;" append ask "CMB,Ground,,,|cyan|gray|magenta|olive|silver|teal;" append ask "CMB,Form,Form type,,|T:Texts|B:Buttons|C:Combo boxes|F:Files and folders|S:Scales and combos;" append ask "Default,Theme:clam,Ground:teal;" formGen[list $ask cBack]
- 直接从命令行,例如
tclsh fgen.tcl "T,name,User name;P,psw,Enter password" data.json
- 通过另一种语言
与其他语言一起使用表单生成器
这些示例处理一个表单(见上图),该表单可用于测试小部件,也可用于探索不同的表单背景和主题。
当表单关闭时,数据以 Json 格式发送到标准输出,以便转换为宿主语言的内部变量;以下是文件params.txt中包含的用于创建表单的列表
CMB,Theme;# themes are set at the beginning with the available themes;
Labels,Right, :;
Comment,Hexadecimal color value must have the form #RRGGBB;
CMT,Ground,,,|cyan|gray|magenta|olive|silver|teal|#C0D0E0;
CMB,Form,
Form examples,,|Texts|Buttons|Combo boxes|Files and folders|Scales and Check box;
T,try,Try this,400;
Default,Theme:clam,Ground:teal;
Validate,Ground is ^#[0-9a-fA-F]{6}$,Not valid color;
Default,try:C#2CThis is a form for test some widgets\nCondor Informatique - Turin#3b
CMB#2CGround#2C#2C#2C|cyan|gray|magenta|olive|silver|teal|#C0D0E0;
在使用 TK FormGen 测试的所有语言中,表单都是通过外部命令执行 TCL 解释器生成的,数据从标准输出读取并转换为内部变量。
发送的命令是:tclsh fGen.tcl params.txt handler.tcl::tryForm handler.tcl::handle
,其中tclsh
是 Tcl 解释器,fGen.tcl
是一个 Tcl 脚本,如果使用适当的参数调用,它会生成一个表单。handler.tcl
包含一个过程handle
,用于管理事件,特别是组合框 Form 的事件Start
和事件Select
。当表单关闭时,会调用过程tryForm
,它会从字段try
中获取数据并生成一个新表单。
namespace eval handler {
variable examplesMap [dict create \
"Texts" "Text,Text;P,psw,Password,,Almost 6 characters;T,
Mail,E mail,18;N,Number;IN,Integer,Integer Number;\
DN,DecNumber,Decimal Number;A,textArea,Text area,5;;
Default,Mail:ross@lib.it,Integer:7,Number:-11,DecNumber:3.1415;" \
"Scales and Check box" "CMB,greeck,Greek letters,,
A:Alfa|Beta|G:Gamma|Delta;CMT,spinBox,Combo and text,,
A:Alfa|Delta|G:Gamma|Omega|T:Tau;\
Scale,Scale2,Scale 2,222,1 -1;QS,Urgency,,,
White|Green|Yellow|Red;Default,Scale2:-0.35,Urgency:Yellow,ckBox:1; \
Check,ckBox,Check box,,Accept news;Check,ckBox2,Check box2,,Agree;" \
"Buttons" "P,psw,Password,18;RDB,Gender,,,M:Male|F:Female|
U:Unspecified;B,infoButton,\u24d8,,infoPsw;After,infoButton,psw;\
B,Start,\u270E;B,fg_Cancel,\u2718;B,fg_Reset,\u21B6;
Default,Gender:M;Required,psw;Validate,psw is .{6#2c},Password too short;\
RDB,Langue,,,us:img\\uss.png|fr:img\\frs.png;"\
"Combo boxes" "CMB,greeck,Greek letters,,A:Alfa|Beta|G:Gamma|Delta;CMT,
spinBox,Combo and text,,A:Alfa|Delta|G:Gamma|Omega|T:Tau;"\
"Files and folders" "File,File,,40,Images:*.gif *.jpg *.png,All:*;
Default,File:C:\\www\\condorinformatique\\nodejs\\tcl;D,
Folder,,30;WF,wFile,Write File;"]
}
proc handle {e} {
lassign $e event path name
if {$event == "Start"} {
# populate the combo Theme with themes supported
fg_setCombo Theme "[join [ttk::style theme names] "|"]"
}
if {$name == "Form" && $event == "Select"} {
# Insert in text area a sample of form
if {[valueOf $name] != ""} {setValue try
"[dict get $::handler::examplesMap [valueOf $name]]"}
}
}
proc tryForm {data} {
append prefix "Title,Form example of " [dict get $data "Form"] ";Ground,"
[dict get $data "Ground"] "," [dict get $data "Theme"] ";Labels,Right, :;"
set systemTime [clock seconds]
append prefix "H,hField,[clock format $systemTime];" [dict get $data "try"]
formGen [list "$prefix"]
}
下面是一个输出示例 (Ruby)。
>ruby tcltk.rb
{
"fg_Button" => "Ok",
"Text" => "Text",
"psw" => "Lafayette77",
"Mail" => "ross@lib.it",
"Number" => -11,
"Integer" => 17,
"DecNumber" => 3.1415,
"textArea" => "big\ttext\n\n"
}
Sum: 9.141500
>Exit code: 0
与语言一起使用
C++
在 C++ 11 中使用 FGen 最为费力;选择 C++ 而不是 C 是因为它支持正则表达式和map
结构。
我没有使用 Json 库,以避免包含第三方库,另一方面,我只需要读取一个简单的键值结构,所以我更喜欢使用正则表达式来提取键值对并将它们插入到map
中(函数json2Map
)。
// C++ 11
#include <bits stdc="">
using namespace std;
// function to find all the matches
map<string, string=""> <code>json2Map</code>(string jsonData) {
map<string, string=""> dataMap;
string regExspr("\\\"(\\w+)\\\":\\s*(\\\"([^\"]*)\\\"|(([+-]?\\d*)(\\.\\d+)?))");
regex re(regExspr);
for (sregex_iterator it = sregex_iterator
(jsonData.begin(), jsonData.end(), re);it != sregex_iterator(); it++) {
smatch match;
match = *it;
int indexValue = 3;
if (match.str(3) == "") indexValue = 2;
cout << match.str(1) << "\t" << match.str(indexValue) << endl;
dataMap[match.str(1)] = match.str(indexValue);
}
return dataMap;
}
int main( int numberArgs, char* argsList[] ) {
vector<string> params{ "fGen.tcl","T,t1;N,n1", "", "" };
int commandLength = 40; // roughly default + fixed (i.e. tclsh ... >j.json)
for (int i=1; i<numberargs; i=""> j.json", params[0].c_str(),
params[1].c_str(),params[2].c_str(),params[3].c_str());
int status = system(tcl);
ifstream jsonFile("j.json");
string jsonData {istreambuf_iterator<char>(jsonFile), istreambuf_iterator<char>()};
cout << jsonData << endl;
map<string, string=""> dataMap = json2Map(jsonData);
if (dataMap.count("Integer") > 0) {
cout << "Sum: " << (atof(dataMap["Integer"].c_str()) +
atof(dataMap["Number"].c_str()) + atof(dataMap["DecNumber"].c_str())) << endl;
}
return 0;
}
节点
const args = process.argv.slice(2)
const {exec} = require('child_process');
var command = 'tclsh ' + args[0] + ' "' + args[1] + '" ' + args[2] + ' "' + args[3]
const tcl = exec(command, function (error, stdout, stderr) {
if (error) {
console.error(`exec error: ${error}`);
return;
}
console.log('Child Process STDOUT: '+stdout);
});
let jsonData = '';
tcl.stdout.on('data', (chunk) => {
jsonData += chunk.toString();
});
tcl.on('exit', () => {
console.log(jsonData);
var data = JSON.parse(jsonData) // the data becomes a JavaScript object
if ("Integer" in data) {
console.log("Sum " + (data.Integer + data.Number + data.DecNumber))
}
});
Python
import os
import subprocess
import json
def main():
pass
os.chdir("C:\\Sviluppo\\fGenTk\\")
p = subprocess.Popen("tclsh fGen.tcl params.txt handler.tcl::tryForm handler.tcl::handle",
shell=True, stdout=subprocess.PIPE)
stdout, stderr = p.communicate()
data = json.loads(stdout.decode('utf-8'))
print(data)
if 'Integer' in data:
print ("Sum: {}".format(data['Integer'] + data['Number'] + data['DecNumber']))
if __name__ == '__main__':
main()
R
library("rjson")
setwd("C:\\Sviluppo\\fGenTk")
parameters <- c("fGen.tcl", "params.txt", "handler.tcl::tryForm", "handler.tcl::handle")
json <- system2("tclsh",parameters,stdout=TRUE)
tmp <- tempfile()
cat(json,file = tmp)
data <- fromJSON(readLines(tmp))
data$fg_Button
if ("Integer" %in% names(data))
cat("Sum:",data$Integer + data$Number + data$DecNumber,"\n")
Ruby
Ruby 可能是使用 TK Fgen 最简单的,只需两条指令,我们就可以得到form
和data
require 'json'
require 'FileUtils'
FileUtils.cd('c:/Sviluppo/fGenTk')
output = `tclsh fGen.tcl params.txt handler.tcl::tryForm handler.tcl::handle`
data = JSON.parse(output) if output != ""
puts JSON.pretty_generate(data).gsub(":", " =>")
if data.has_key?("Integer") then
puts sprintf("Sum: %f",data['Integer'] + data['Number'] + data['DecNumber'])
end
注意
- ^ 这是我 网站上可以找到的一些表单生成器之一(适用于 Autoit、Powershell、B4A、B4J 和 JavaScript)。
此外,CodeProject 上还有一些文章 - ^ 软件包和文档可以在这里下载。
历史
- 2019 年 9 月 30 日:初始版本